package modules.loader
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	
	import mx.containers.VBox;
	import mx.events.ModuleEvent;
	import mx.modules.IModuleInfo;
	import mx.modules.ModuleManager;
	
	import sdk3.c_OBJECT;
	import sdk3.c_formaters;
	
	public class c_ModuleLoader extends EventDispatcher
	{
		public function c_ModuleLoader(target:IEventDispatcher=null)
		{
			super(target);
		}
		
		/** reference to interface & instance of MODUL
		 * INTERFACE
		 * -> is of Type IModuleInfo provides an interface to load, unload and communicate with the module.
		 * INSTANCE
		 * -> is a reference to an instance of the module
		 * CONTAINER
		 * -> is a reference to the container of the module
		 * PARAMETER
		 * -> is an Object that is passed to the module for initialization.
		 * NAME
		 * -> is the title of the tab of the TabNavigator
		 * INDEX
		 * -> is used to keep track to what tabnavigator-index or viewstack-index the module was asigned.
		 **/
		public var MODULE_INTERFACES:Object = {MODULEURL:{INTERFACE:null             ,INSTANCE:null        ,CONTAINER:null, PARAMETER:null, NAME:'', INDEX:0}};
		
		public var c_OBJECT_inst:c_OBJECT = new c_OBJECT();
		public var c_formaters_inst:c_formaters = new c_formaters();
		
		/** name of the module-url that is currently beeing loaded **/
		public var CURRENT_LOADING_MODULE:String = '';
		
		// wechselt die View vom ViewStack und holt gleichzeitig für die jeweilige mxml benötigte daten per httpservice
		// NUR SO Views AUFRUFEN! NIE DIREKT! Weil sonst die daten fehlen.
		/** creationComplete der einzelnen module wird zu einem unberechenbaren zeitpunkt ausgeführt,
		 d.h. hier nur initialisierungen tätigen welche von keinen anderen initialisierungen abhängig sind.
		 
		 für abhängige initialisierungen eine EnterViewStack funktion in das modul mit aufnehmen,
		 welche von der start -> changeView -> ModulName.EnterViewStack
		 aufgerufen wird.
		 
		 INPUT:
		 * MODULEURL -> pfad + dateiname.swf des aufzurufenden modul.
		 * parent -> das parent welches dieses modul aufgerufen hat.
		 * mode kann folgende werte annehmen:
		 * 'return to parent' -> suche und finde das parent dieses moduls und rufe es auf.
		 * 'activate' -> aufruf an das bereits existierende parent sich zu aktivieren ohne sich neu zu initialisieren (ohne initApp nur focus und addKeyboardEventListener).
		 * 
		 * EXAMPLE:
		 [0] = Object (@13ef0129)	
		 GROUP = "Buchungserfassung"	
		 MODULEURL = "./modules/mxml/MODULE_module1.swf"	
		 NAME = "Buchungserfassung"	
		 PARAMETER = null	
		 STATUS = "ready"	
		 * 
		 * PARAMETER
		 * -> falls an das entsprechende aufzurufende Modul daten übergeben werden müssen, werden diese hier übergeben.
		 **/
		
		public function LOAD_MODULE(MODULEURL:String = '',PARAMETER:* = null,GROUP:String = '',NAME:String = '',STATUS:String = ''):void
		{
			CURRENT_LOADING_MODULE = MODULEURL;
			if(c_OBJECT.singleton().not_empty(MODULEURL))
			{
				// try to activate the named module
				if(MODULE_INTERFACES[MODULEURL] == null)
				{
					MODULE_INTERFACES[MODULEURL] = {INTERFACE:null             ,INSTANCE:null        ,PARAMETER:PARAMETER, GROUP:GROUP, NAME:NAME, STATUS:STATUS, INDEX:0};
					/**                             INTERFACE:new IModuleInfo(),INSTANCE:new Object(),PARAMETER:new Object()
					 * INTERFACE
					 * -> is of Type IModuleInfo provides an interface to load, unload and communicate with the module.
					 * INSTANCE
					 * -> is a reference to an instance of the module
					 * PARAMETER
					 * -> is an Object that is passed to the module for initialization.
					 * NAME
					 * -> is the title of the tab of the TabNavigator
					 * INDEX
					 * -> is used to keep track to what tabnavigator-index or viewstack-index the module was asigned.
					 **/
				}
			}
			if(c_OBJECT.singleton().not_empty(PARAMETER))
			{
				MODULE_INTERFACES[MODULEURL]['PARAMETER'] = PARAMETER;
			}
			if(c_OBJECT_inst.not_empty(GROUP))
			{
				MODULE_INTERFACES[MODULEURL]['GROUP'] = GROUP;
			}
			if(c_OBJECT_inst.not_empty(NAME))
			{
				MODULE_INTERFACES[MODULEURL]['NAME'] = NAME;
			}
			if(c_OBJECT_inst.not_empty(STATUS))
			{
				MODULE_INTERFACES[MODULEURL]['STATUS'] = STATUS;
			}
			
			/** -> suche und finde das parent dieses moduls und rufe es auf. **/
			if(MODULE_INTERFACES[MODULEURL]['INSTANCE'] == null) // check ob bereits eine instanz dieses popups erzeugt wurde
			{
				/** modul existiert noch nicht, erstelle eine neue instanz es moduls & lade modul **/
				MODULE_INTERFACES[MODULEURL]['INTERFACE'] = ModuleManager.getModule(MODULEURL);
				MODULE_INTERFACES[MODULEURL]['INTERFACE'].addEventListener(ModuleEvent.SETUP, CREATE_INSTANCE_OF_MODUL, false, 0, true);
				MODULE_INTERFACES[MODULEURL]['INTERFACE'].addEventListener(ModuleEvent.PROGRESS, CREATE_INSTANCE_OF_MODUL, false, 0, true);
				MODULE_INTERFACES[MODULEURL]['INTERFACE'].addEventListener(ModuleEvent.READY, CREATE_INSTANCE_OF_MODUL, false, 0, true);
				MODULE_INTERFACES[MODULEURL]['INTERFACE'].addEventListener(ModuleEvent.UNLOAD, CREATE_INSTANCE_OF_MODUL, false, 0, true);
				MODULE_INTERFACES[MODULEURL]['INTERFACE'].addEventListener(ModuleEvent.ERROR, CREATE_INSTANCE_OF_MODUL, false, 0, true);
				MODULE_INTERFACES[MODULEURL]['INTERFACE'].load();
			}
			else
			{
				try
				{
					MODULE_INTERFACES[MODULEURL]['INSTANCE'].initApp(); // -> initialisierung des popups zB. addKeyboardEventListener(); etc.
				}
				catch(e:Error)
				{
					trace("c_ModuleLoader.as : Error, function initApp() not implemented by Module "+MODULEURL);
				}
				/** modul existiert bereits, muss also im Container nur angezeigt werden bzw. in Fordergrund gestellt werden. **/
				/** instance of modul allready exists, needs to be brought in the foreground. **/
				this.dispatchEvent(new Event("ready"));
			}
		}

		/** unloads the given module, 
		 * RESULT:
		 var RESULT:Object = {INDEX:'',STATUS:''};
		 * -> **/
		public function UNLOAD_MODULE(MODULEURL:String):Object
		{
			var RESULT:Object = {INDEX:'',STATUS:''};
			if(c_OBJECT.singleton().not_empty(MODULEURL))
			{
				/**
				 *  Unloads the module.
				 *  Flash Player and AIR will not fully unload and garbage collect this module if
				 *  there are any outstanding references to definitions inside the
				 *  module.
				 */
				if(MODULE_INTERFACES[MODULEURL] != null)
				{
					MODULE_INTERFACES[MODULEURL]['INSTANCE'] = null;
					MODULE_INTERFACES[MODULEURL]['INTERFACE'].removeEventListener(ModuleEvent.SETUP, CREATE_INSTANCE_OF_MODUL);
					MODULE_INTERFACES[MODULEURL]['INTERFACE'].removeEventListener(ModuleEvent.PROGRESS, CREATE_INSTANCE_OF_MODUL);
					MODULE_INTERFACES[MODULEURL]['INTERFACE'].removeEventListener(ModuleEvent.READY, CREATE_INSTANCE_OF_MODUL);
					MODULE_INTERFACES[MODULEURL]['INTERFACE'].removeEventListener(ModuleEvent.UNLOAD, CREATE_INSTANCE_OF_MODUL);
					MODULE_INTERFACES[MODULEURL]['INTERFACE'].removeEventListener(ModuleEvent.ERROR, CREATE_INSTANCE_OF_MODUL);
					MODULE_INTERFACES[MODULEURL]['INTERFACE'].unload();
					MODULE_INTERFACES[MODULEURL]['INTERFACE'] = null;
					
					RESULT['STATUS'] = MODULE_INTERFACES[MODULEURL]['STATUS'] = "unloaded";
					RESULT['INDEX'] = MODULE_INTERFACES[MODULEURL]['INDEX'];
					
					MODULE_INTERFACES[MODULEURL] = null;
				}
			}
			
			return RESULT;
		}
		
		/**
		 * erstellt eine instanz, speichert referenz dazu in einer public var MODULE_... richtet quasi den Zugriff auf das Modul ein. 
		 * Get the IModuleInfo interface for the specified URL. **/
		public function CREATE_INSTANCE_OF_MODUL(event:ModuleEvent):void
		{
			if(event.type == ModuleEvent.SETUP)
			{
				trace(".....ModuleEvent: type:" + event.type + " message: setting up Module. ");
			}
			else if(event.type == ModuleEvent.PROGRESS)
			{
				/** display loading progress on console **/
				trace(".......ModuleEvent: type: " + event.type + " status: "+c_formaters_inst.FormatFloat(event['bytesLoaded'] / (event['bytesTotal'] / 100)) + "% loading module: " + CURRENT_LOADING_MODULE);
			}
			else if(event.type == ModuleEvent.READY)
			{
				trace(".........ModuleEvent: type: " + event.type + " message: module ready: " + CURRENT_LOADING_MODULE);
				// Get an instance of the module.
				for(var MODULEURL:String in MODULE_INTERFACES)
				{
					if(MODULEURL != 'MODULEURL') // 'MODULEURL' is just used for showing the layout of the MODULE_INTERFACES object.
					{
						if(MODULE_INTERFACES[MODULEURL] != null)
						{
							if(MODULE_INTERFACES[MODULEURL]['INTERFACE']['ready'])
							{
								if(MODULE_INTERFACES[MODULEURL]['INSTANCE'] == null) 
								{
									try
									{
										var factory:IFactoryModule = MODULE_INTERFACES[MODULEURL]['INTERFACE'].factory.create() as IFactoryModule;
										// create instance from the factory
										MODULE_INTERFACES[MODULEURL]['INSTANCE'] = factory.create_instance(MODULE_INTERFACES[MODULEURL]['PARAMETER']); // pass parameter while instanciating the module
										
										this.dispatchEvent(new Event("ready"));
									}
									catch(e:Error)
									{
										var ERROR:String =  " c_ModuleLoader.as : failed to instantiate the module "+MODULEURL+", \n does the module implement <mx:Module implements=\"modules.loader.IFactoryModule\"/> (including public function create_instance(PARAMETER:*):*?) ErrorMessage: "+e;
										/**
										<?xml version="1.0" encoding="utf-8"?>
										<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml"
												   label="empty"
												   layout="absolute"
												   width="100%"
												   height="100%"
												   implements="modules.loader.IFactoryModule"
												   >
											<mx:Script>
												<![CDATA[
													// this parameter is temporarily stored here, as soon as create_instance is called,
													// it will processed by initApp as soon as creationComplete is done
													// * OPs -> liste von OPs aus welchen BuchenViewen aufgebaut werden sollen.
													// * Datum -> Datum welches für die BuchenView verwendet werden soll
													// * Periode -> Periode welches für die BuchenView verwendet werden soll
													public var INIT_PARAM:* = {OPs:new Array(),Datum:'',Periode:''};
													
													// create instance of this module
													public function create_instance(PARAMETER:*):*
													{
														var MODUL_instance:MODULE_module1 = new MODULE_module1();
														INIT_PARAM = PARAMETER;
														return MODUL_instance;
													}
												]]>
											</mx:Script>
										</mx:Module>
										**/
										throw new Error(ERROR);
									}
								}
							}
							else
							{
								// tue nichts, reference auf instance ist schon eingerichtet.
							}
						}
					}
				}
			}
			else if(event.type == ModuleEvent.UNLOAD)
			{
				trace(".................ModuleEvent: " + event.type);
			}
			else if(event.type == ModuleEvent.ERROR)
			{
				trace(".................ModuleEvent: " + event.type +" while loading module: " + CURRENT_LOADING_MODULE);
				trace(". errorText: " + event['errorText']);
				trace(".............................. " );
				throw new Error(event['errorText']);
			}
		}
		
		/** get access to the given module, if null is returned this means that the current module was not loaded.
		 * 
		 * INPUT:
		 * MODULEURL -> all references are stored inside MODULE_INTERFACES and only accessible via the unique MODULEURL
		 * MODULENAME -> the name of the module how it will be displayed on the TabLabel in the TabNavigator (optional)
		 * **/
		public function GET_REFERENCE_TO_MODULE(MODULEURL:String,NAME:String = ''):*
		{
			MODULE_INTERFACES[MODULEURL]['NAME'] = NAME; // the name of the module how it will be displayed on the TabNavigator
			return MODULE_INTERFACES[MODULEURL]['INSTANCE'];
		}
	}
}